import { EventService } from 'src/modules/event/event.service';
import logger from 'src/common/utils/log';
import { PlayerSignup } from '../entities/player-signup.entity';
import { TournamentService } from 'src/modules/tournament/tournament.service';
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import {
  EventTypeText,
  EventText2Type,
  maleEventList,
  femaleEventList,
  TeamEventList,
  DoubleEventList,
  SingleEventList,
  playerSignupTypeEnum,
} from 'src/common/constant/enums';
import excelToObject from 'src/common/utils/excelToObject';
import { getE2PCount } from 'src/common/utils/matchUtils';
import { Category } from 'src/modules/category/entities/category.entity';
import { StageEvent } from 'src/modules/stage-event/entities/stage-event.entity';
import { Tournament } from 'src/modules/tournament/entities/tournament.entity';
import {
  assign,
  keyBy,
  merge as lodashMerge,
  groupBy,
  remove,
  mergeWith,
  flatMapDeep,
  uniqWith,
} from 'lodash';
import { Event } from 'src/modules/event/entities/event.entity';
import { PlayerSignupService } from './player-signup.service';

export type SignupPlayerType = {
  _order: string;
  pairName: string;
  playerInfo: {
    name: string;
    gender: string;
    age: string;
    idCard: string;
    phone: string;
    organization: string;
    isMainPlayer: boolean;
  };
  stageEventInfo: { [eventName: string]: string };
  categoryInfo: { [categoryName: string]: string };
};
export type SignupExcelDataType = {
  sheetName: string;
  data: SignupPlayerType[];
}[];
export type TournamentInfoMap = {
  [cname: string]: {
    category: Category;
    event: Event;
    stageEvent?: StageEvent;
  };
};

@Injectable()
export class CreateSignupService {
  constructor(
    private tournamentService: TournamentService,
    // private eventService: EventService,
    private playerSignupService: PlayerSignupService,
  ) {}
  // 根据报名信息，生成抽签信息
  getConfirmE2PParam(savedSignupList: PlayerSignup[]) {
    const playsWithOrderGroupbyEventId: {
      [eventId: string]: (PlayerSignup & {
        _order?: number;
      })[];
    } = groupBy(
      flatMapDeep(savedSignupList, (item: PlayerSignup) => {
        if (item.subPlayerSignups?.length > 0) {
          return [item, ...item.subPlayerSignups];
        }
        return item;
      }).filter((item) => {
        // return !isNaN(Number(item._order));
        return typeof item._order === 'number' && item._order >= 0;
      }),
      (v) => {
        return v.eventId || v.event.id;
      },
    );
    const confirmE2PParamList: [number, { [order: number]: number[] }][] = [];
    Object.entries(playsWithOrderGroupbyEventId).forEach(
      ([eventId, playersWithOrder]) => {
        const { type: eventType } = playersWithOrder[0].event;
        const confirmE2PParam: { [order: number]: number[] } = {};
        if (SingleEventList.includes(eventType)) {
          playersWithOrder.forEach((player) => {
            if (confirmE2PParam[player._order]) {
              throw new HttpException(
                `${player.name} 坑位 ${player._order} 被重复定义(单打)`,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
            confirmE2PParam[player._order] = [player.id];
          });
        } else if (DoubleEventList.includes(eventType)) {
          playersWithOrder.forEach((player) => {
            if (
              confirmE2PParam[player._order] &&
              !confirmE2PParam[player._order].includes(player.id)
            ) {
              throw new HttpException(
                `${player.name} 坑位 ${player._order} 被定义为不同值(双打)`,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
            // 找到所有同队的队员
            confirmE2PParam[player._order] = savedSignupList
              .filter((item) => {
                const itemEventId = item.eventId || item.event?.id;
                return (
                  itemEventId === Number(eventId) &&
                  item.pairName === player.pairName
                );
              })
              .map((item) => {
                return item.id;
              });
          });
        } else if (TeamEventList.includes(eventType)) {
          const playersTeamIdMap: { [playerId: number]: number } = {};
          savedSignupList.forEach((player) => {
            const { id: teamId, subPlayerSignups } = player;
            subPlayerSignups.forEach((subPlayer) => {
              playersTeamIdMap[subPlayer.id] = teamId;
            });
          });
          playersWithOrder.forEach((player) => {
            const { id: playerId, _order } = player;
            const teamPlayerIds = playersTeamIdMap[playerId];
            if (
              confirmE2PParam[_order] &&
              confirmE2PParam[_order][0] !== teamPlayerIds
            ) {
              throw new HttpException(
                `${player.name} 坑位 ${player._order} 被定义为不同值(团体)`,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
            confirmE2PParam[_order] = [teamPlayerIds];
          });
        }
        confirmE2PParamList.push([Number(eventId), confirmE2PParam]);
      },
    );
    return confirmE2PParamList;
  }

  private getTournamentInfoMap(tournamentDetail: Tournament) {
    const infoMap: TournamentInfoMap = {};
    tournamentDetail.categories.forEach((category) => {
      const { events } = category;
      events.forEach((event) => {
        const key = `${category.name}-${event.type}`;
        infoMap[key] = {
          category,
          event,
        };
        const stages = event.stages;
        if (stages?.length) {
          stages.forEach((stage) => {
            const { stageEvents } = stage;
            if (stageEvents?.length) {
              stageEvents.forEach((stageEvent) => {
                const key = `${category.name}-${event.type}-${stage.stageOrderInEvent}-${stageEvent.type}-${stageEvent.eventOrder}`;
                infoMap[key] = {
                  category,
                  event,
                  stageEvent,
                };
              });
            }
          });
        }
      });
    });
    return infoMap;
  }
  async getPlayerSignupFromExcel(
    tournamentId: number,
    file: Express.Multer.File,
    stageIndex = 0,
  ): Promise<(PlayerSignup & { _order?: number })[]> {
    const excelData: SignupExcelDataType = await excelToObject(file.buffer, {
      headerKeyMap: {
        坑位: '_order',
        双打队名: 'pairName',
        选手: 'playerInfo',
        项目: 'stageEventInfo',
        组别: 'categoryInfo',
        姓名: 'name',
        性别: 'gender',
        年龄: 'age',
        身份证号: 'idCard',
        电话: 'phone',
        单位: 'organization',
        是否上场: 'isMainPlayer',
      },
      headerRowCount: 2,
    });
    // 生成赛季基本新的一个速查表，方便遍历
    const tournamentDetail =
      await this.tournamentService.findDetailById(tournamentId);
    const infoMap = this.getTournamentInfoMap(tournamentDetail);
    const keys = Object.keys(infoMap);
    if (!keys || !keys.length) {
      // throw new HttpException(
      //   `赛事信息不足，请确认是否创建了赛事阶段:  ${tournamentDetail.name}(${tournamentId})`,
      //   HttpStatus.INTERNAL_SERVER_ERROR,
      // );
    }
    logger.info(
      `getPlayerSignupFromExcel tournamentId: ${tournamentId} infoMap: ${keys.join(',')}`,
    );
    return this.getPlayerSignups(excelData, infoMap, stageIndex);
  }
  getPlayerSignups = async (
    excelData: SignupExcelDataType,
    infoMap: TournamentInfoMap,
    stageIndex = 0,
  ) => {
    // 开始生成signup信息
    const signupList: PlayerSignup[] = [];
    const genPlayerSignup = (
      sheetName: string,
      key: string,
      excelData: SignupPlayerType,
    ): PlayerSignup => {
      const { playerInfo, pairName, _order } = excelData;
      playerInfo.isMainPlayer = !!playerInfo.isMainPlayer;
      logger.info(
        `genPlayerSignup sheetName: key: ${key}, playerName: ${playerInfo.name} 是否上场 ${playerInfo.isMainPlayer}`,
      );
      if (!infoMap[key]) {
        const tournamentInfoArr = [];
        const keys = key.split('-');
        keys.forEach((key, index) => {
          if (index === 0) {
            // 0 是category
            tournamentInfoArr.push(key);
          } else {
            // 其他都是event
            const keyName = EventTypeText[key];
            tournamentInfoArr.push(keyName);
          }
        });
        throw new HttpException(
          `sheetName: ${sheetName} ${playerInfo.name} 报名赛事错误 ${tournamentInfoArr.join(' ')} key: ${key}`,
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
      const playerSignup: PlayerSignup = assign(
        new PlayerSignup(),
        playerInfo,
        infoMap[key],
      );
      playerSignup.pairName = pairName;
      playerSignup.key = playerInfo.idCard;
      if (parseInt(_order) >= 0) {
        playerSignup._order = parseInt(_order);
      }
      return playerSignup;
    };
    excelData.forEach((item) => {
      const { sheetName, data: signupExcelDataList } = item;
      logger.info(
        `getPlayerSignupFromExcel sheetName, signupExcelDataList.length： ${sheetName} ${signupExcelDataList.length}`,
      );
      const eventType = EventText2Type[sheetName];
      if (!eventType) {
        throw new HttpException(
          `sheetName ${sheetName} 非法`,
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
      signupExcelDataList.forEach((signupExcelData) => {
        const { categoryInfo, stageEventInfo } = signupExcelData;
        if (!categoryInfo) {
          throw new HttpException(
            `sheetName ${sheetName} 缺少组别信息`,
            HttpStatus.INTERNAL_SERVER_ERROR,
          );
        }
        // const { name: categoryName } = categoryInfo;
        const signupCategoryNames = [];
        Object.entries(categoryInfo).forEach(([categoryName, value]) => {
          if (!value) {
            return;
          }
          signupCategoryNames.push(categoryName);
        });
        const signupStageEventNames = [];
        if (stageEventInfo) {
          Object.entries(stageEventInfo).forEach(([stageEventName, value]) => {
            if (!value) {
              return;
            }
            if (!stageEventName.includes('-')) {
              throw new HttpException(
                `sheetName ${sheetName} 团赛项目格式需要满足: 项目名-顺序`,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
            signupStageEventNames.push(stageEventName);
          });
        }
        signupCategoryNames.forEach((categoryName) => {
          // 遍历组别
          if (!signupStageEventNames.length) {
            // 非团队赛
            const key = `${categoryName}-${eventType}`;
            signupList.push(genPlayerSignup(sheetName, key, signupExcelData));
          } else {
            signupStageEventNames.forEach((stageEventName) => {
              const [eventTypeName, eventIndex] = stageEventName.split('-');
              const stageEventType = EventText2Type[eventTypeName];
              if (!stageEventType) {
                throw new HttpException(
                  `sheetName：${sheetName} stageEventName：${stageEventName} 非法`,
                  HttpStatus.INTERNAL_SERVER_ERROR,
                );
              }
              const key = `${categoryName}-${eventType}-${stageIndex}-${stageEventType}-${eventIndex}`;
              if (!infoMap[key]) {
                const shouldBePlace = [];
                Object.values(infoMap).forEach((v) => {
                  if (v.stageEvent.type === stageEventType) {
                    shouldBePlace.push(v.stageEvent.eventOrder + 1);
                  }
                });
                throw new HttpException( // 5. 多个相同stageEvent：需要验证报名表的stageEnvent 顺序和 配置的顺序一致
                  `stageEventName：${stageEventName} 不存在 或者顺序不在 ${shouldBePlace.join(',')} 位 (${key})`,
                  HttpStatus.INTERNAL_SERVER_ERROR,
                );
              }
              signupList.push(genPlayerSignup(sheetName, key, signupExcelData));
            });
          }
        });
      });
      // aaaaaaa
      const uniqSignupList = uniqWith(
        signupList,
        (a: PlayerSignup, b: PlayerSignup) => {
          // 去重相同选手
          const aStageEventId = a.stageEvent?.id || a.stageEventId;
          const bStageEventId = b.stageEvent?.id || b.stageEventId;
          return a.key === b.key && aStageEventId === bStageEventId;
        },
      );
      if (TeamEventList.includes(eventType)) {
        // 以团队的维度重新整合比赛
        const _signupList = [...uniqSignupList];
        remove(signupList, () => true); // 清空数组,重新填充team 数据
        const groupByOrgEvent = groupBy(_signupList, (v: PlayerSignup) => {
          return `${v.organization}-${v.event.id}`;
        });
        Object.entries(groupByOrgEvent).forEach(([key, players]) => {
          // const [organization, eventId] = key.split('-');
          const organization = players[0].organization;
          const eventId = players[0].event.id;
          const teamPlayer = new PlayerSignup();
          teamPlayer.name = organization;
          teamPlayer.organization = organization;
          teamPlayer.event = players[0].event;
          teamPlayer.eventId = parseInt(eventId);
          // 只要player中有人是主选手，则该团队就被认为是主选手
          teamPlayer.isMainPlayer = (players as Array<any>).some(
            (player) => player.isMainPlayer,
          );
          teamPlayer.type = playerSignupTypeEnum.team;
          teamPlayer.key = `${organization}-${eventId}`;
          teamPlayer.subPlayerSignups = players as PlayerSignup[];
          signupList.push(teamPlayer);
        });
      }
    });
    logger.info(
      `getPlayerSignupFromExcel before validate signupList.length: ${signupList.length}`,
    );
    const signupGroupByEventId: { [eventId: number]: PlayerSignup[] } = groupBy(
      signupList,
      (v) => {
        return v.eventId || v.event?.id;
      },
    );
    const promiseList = Object.entries(signupGroupByEventId).map(
      ([eventId, players]) => {
        return this.mergePlayerSignup(parseInt(eventId), players); // 与数据库数据合并
      },
    );
    const promiseListRes = await Promise.all(promiseList);
    const mergedPlayerList = promiseListRes.flat();
    this.validatePlayerSignupList(mergedPlayerList);

    return mergedPlayerList;
  };
  validatePlayerSignupList(signupList: PlayerSignup[]) {
    const signupMapByEventId: { [eventId: number]: PlayerSignup[] } = groupBy(
      signupList,
      // 'eventId',
      (v) => {
        return v.eventId || v.event?.id;
      },
    );
    Object.entries(signupMapByEventId).forEach((v) => {
      const [, signups] = v;
      const mainPlayers = signups.filter((signup) => signup.isMainPlayer);
      if (!mainPlayers || !mainPlayers.length) {
        return;
      }
      const { event } = mainPlayers[0];
      const { stages, type: eventType, name: eventName } = event;
      const isMaleMatch = maleEventList.includes(eventType);
      const isFemaleMatch = femaleEventList.includes(eventType);
      let maxPlayerCount = 0;
      const stageEvents = [];
      stages.forEach((stage) => {
        const { type: stageType, playerCount, roundGroupCount } = stage;
        const stageAllowPlayMaxCount = getE2PCount(
          stageType,
          playerCount,
          roundGroupCount,
        );
        maxPlayerCount =
          stageAllowPlayMaxCount > maxPlayerCount
            ? stageAllowPlayMaxCount
            : maxPlayerCount;
        if (stage.stageEvents?.length) {
          stage.stageEvents.forEach((stageEvent) => {
            stageEvents.push(stageEvent);
          });
        }
      });
      if (TeamEventList.includes(eventType)) {
        // 1. 人数超了，不能报名，
        if (mainPlayers.length / 2 > maxPlayerCount) {
          // throw new HttpException( // 5. 多个相同stageEvent：需要验证报名表的stageEnvent 顺序和 配置的顺序一致
          //   `${eventName} 报名人数超过限制 ${mainPlayers.length} / ${maxPlayerCount}`,
          //   HttpStatus.INTERNAL_SERVER_ERROR,
          // );
        }
        // 2. 性别不符不能报名
        const teamInfo: { [teamName: string]: [PlayerSignup?, PlayerSignup?] } =
          {};
        mainPlayers.forEach((player) => {
          const { subPlayerSignups } = player;
          subPlayerSignups.forEach((subPlayerSignup) => {
            const { gender, stageEvent, idCard, organization } =
              subPlayerSignup;
            const { type: stageEventType } = stageEvent || {};
            if (!idCard || !organization) {
              throw new HttpException(
                `${subPlayerSignup.name} 报名没有身份证号 或 单位`,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
            if (
              (isMaleMatch && gender !== 'male') ||
              (isFemaleMatch && gender !== 'female')
            ) {
              // 2. 性别不符不能报名
              throw new HttpException(
                `${subPlayerSignup.name} 报名性别不符 ${gender}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
            const isDoubleMatch = DoubleEventList.includes(stageEventType);
            if (isDoubleMatch) {
              if (!subPlayerSignup.pairName) {
                throw new HttpException(
                  `${subPlayerSignup.name} 报名没有在团队双打中没有设置teamName`,
                  HttpStatus.INTERNAL_SERVER_ERROR,
                );
              }
              const key = `${subPlayerSignup.pairName}-${subPlayerSignup.stageEvent.id}`;
              teamInfo[key] = teamInfo[key] || [];
              teamInfo[key].push(subPlayerSignup);
            }
          });
          Object.entries(teamInfo).forEach((v) => {
            const [teamName, players] = v;
            if (players.length !== 2) {
              throw new HttpException(
                `${teamName} 双打人数不为2: ${players.map((v) => v.name).join(', ')}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
          });
        });
        // 3. 双打人数为单数不能报名，
        // 4. 双打团赛没有teamName 不能报名
        // 5.双打不能有三个人的teamName一致
      } else if (DoubleEventList.includes(eventType)) {
        // 1. 人数超了，不能报名，
        if (mainPlayers.length / 2 > maxPlayerCount) {
          // throw new HttpException( // 5. 多个相同stageEvent：需要验证报名表的stageEnvent 顺序和 配置的顺序一致
          //   `${eventName} 报名人数超过限制 ${mainPlayers.length} / ${maxPlayerCount * 2}`,
          //   HttpStatus.INTERNAL_SERVER_ERROR,
          // );
        }
        const teamInfo: { [teamName: string]: [PlayerSignup?, PlayerSignup?] } =
          {};
        mainPlayers.forEach((signup) => {
          const { gender, idCard, organization } = signup;
          if (!idCard || !organization) {
            throw new HttpException(
              `${signup.name} 报名没有身份证号 或 单位`,
              HttpStatus.INTERNAL_SERVER_ERROR,
            );
          }
          if (
            (isMaleMatch && gender !== 'male') ||
            (isFemaleMatch && gender !== 'female')
          ) {
            // 2. 性别不符不能报名
            throw new HttpException(
              `${signup.name} 报名性别不符 ${gender}`,
              HttpStatus.INTERNAL_SERVER_ERROR,
            );
          }
          // 4. 双打团赛没有teamName 不能报名
          if (!signup.pairName) {
            throw new HttpException(
              `${signup.name} 报名没有teamName`,
              HttpStatus.INTERNAL_SERVER_ERROR,
            );
          }
          teamInfo[signup.pairName] = teamInfo[signup.pairName] || [];
          teamInfo[signup.pairName].push(signup);
        });
        Object.entries(teamInfo).forEach((v) => {
          const [teamName, players] = v;
          if (players.length !== 2) {
            throw new HttpException(
              `${teamName} 双打人数不为2: ${players.map((v) => v.name).join(', ')}`,
              HttpStatus.INTERNAL_SERVER_ERROR,
            );
          }
        });
        // 3. 双打人数为单数不能报名，
        // 5.双打不能有三个人的teamName一致
      } else if (SingleEventList.includes(eventType)) {
        // 1. 人数超了，不能报名，
        if (mainPlayers.length > maxPlayerCount) {
          // throw new HttpException( // 5. 多个相同stageEvent：需要验证报名表的stageEnvent 顺序和 配置的顺序一致
          //   `${eventName} 报名人数超过限制 ${mainPlayers.length} / ${maxPlayerCount}`,
          //   HttpStatus.INTERNAL_SERVER_ERROR,
          // );
        }
        // 2. 性别不符不能报名
        const isMaleMatch = maleEventList.includes(eventType);
        mainPlayers.forEach((signup) => {
          const { gender, idCard, organization } = signup;
          if (!idCard || !organization) {
            throw new HttpException(
              `${signup.name} 报名没有身份证号 或 单位`,
              HttpStatus.INTERNAL_SERVER_ERROR,
            );
          }
          if (
            (isMaleMatch && gender !== 'male') ||
            (isFemaleMatch && gender !== 'female')
          ) {
            throw new HttpException(
              `${signup.name} 报名性别不符 ${gender}`,
              HttpStatus.INTERNAL_SERVER_ERROR,
            );
          }
        });
      }
    });
  }
  async mergePlayerSignup(eventId: number, newSignups: PlayerSignup[]) {
    const savedSignups = await this.playerSignupService.listByEventId(eventId);
    if (!savedSignups || savedSignups.length === 0) {
      return newSignups;
    }

    savedSignups.forEach((signup) => {
      signup._order = null;
      // signup.isMainPlayer = false; // 不能将他置空
    });
    const savedSignupsByMainField = keyBy(
      flatMapDeep(savedSignups, (v: PlayerSignup) => {
        if (v.subPlayerSignups?.length) {
          return [v, ...v.subPlayerSignups];
        }
        return v;
      }),
      (v: PlayerSignup) =>
        `${v.organization || ''}-${v.idCard || ''}-${v.stageEventId || v.stageEvent?.id || ''}`,
    );
    newSignups.forEach((newPlayer: PlayerSignup) => {
      const playerKey = `${newPlayer.organization || ''}-${newPlayer.idCard || ''}-${newPlayer.stageEventId || newPlayer.stageEvent?.id || ''}`;
      if (savedSignupsByMainField[playerKey]) {
        mergeWith(
          savedSignupsByMainField[playerKey],
          newPlayer,
          (srcVal, toVal, key) => {
            if (key === 'subPlayerSignups') {
              return srcVal;
            }
            return undefined;
          },
        );
        if (newPlayer.subPlayerSignups?.length) {
          // 团队赛
          const teamPlayer = savedSignupsByMainField[playerKey];
          newPlayer.subPlayerSignups.forEach((subPlayer) => {
            const subPlayerKey = `${subPlayer.organization || ''}-${subPlayer.idCard || ''}-${subPlayer.stageEventId || subPlayer.stageEvent?.id || ''}`;
            if (savedSignupsByMainField[subPlayerKey]) {
              lodashMerge(savedSignupsByMainField[subPlayerKey], subPlayer);
            } else {
              teamPlayer.subPlayerSignups.push(subPlayer);
            }
          });
        }
      } else {
        savedSignups.push(newPlayer);
      }
    });
    return savedSignups;
  }
}
